#include <mntent.h>
#include <sys/statfs.h>

#include <QMap>
#include <QDir>
#include <QMenu>
#include <QDebug>
#include <QLabel>
#include <QProcess>
#include <QTimer>
#include <QDateTime>
#include <QPainter>
#include <QSettings>
#include <QTcpSocket>
#include <QMouseEvent>

#include <QTextDocument>

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "setupwindow.h"

const QString htmlHEAD = "<table width=100% height=100% style=\" font-family: '%1'; color: %2; font-size: %3pt; font-weight: %4; font-style: %5; \"><tr><td>";
const QString htmlTAIL = "</td></tr></table>";

const QString defaultFormatDateTime = "$year/$month/$day$CRLF$hour:$minute:$second";
const QString defaultFormatNetIO = "$dev $max$CRLFr:$R w:$W";
const QString defaultFormatDiskIO = "$shortdev $max $temp$CRLFr:$R w:$W";
const QString defaultFormatDiskUsage = "$free/$total $shortmount";
const QString defaultFormatGPU = "GPU($ver) $free/$total $temp";
const QString defaultFormatCPU = "$dev $pcpu $freq $temp";
const QString defaultFormatProcList = "$pcpu $pmem $pid $cmd";

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    loadSettings();

    DiskIOCount = 0;
    NetIOCount = 0;
    CoreCount = 0;
    DiskUsageCount = 0;
    CoreDisplayPos = 0;

    qint64 xx = QDateTime::currentMSecsSinceEpoch();
    msDiskUsageCheck = xx;
    msCPUUsageCheck = xx;
    msDiskIOStateCheck = xx;
    msNetIOStateCheck = xx;
    msGPUTempCheck = xx;
    msDiskTempCheck = xx;
    msCoreTempCheck = xx;
    msProcListCheck = xx;

    ActSetup = new QAction(tr("&Setup"), this);
    ActSetup->setShortcuts(QKeySequence::Preferences);
    ActSetup->setStatusTip(tr("Setup..."));
    connect(ActSetup, SIGNAL(triggered()), this, SLOT(showSetup()));

    ActReload = new QAction(tr("R&eload"), this);
    ActReload->setShortcuts(QKeySequence::Refresh);
    ActReload->setStatusTip(tr("Reload Settings"));
    connect(ActReload, SIGNAL(triggered()), this, SLOT(loadSettings()));

    ActClose = new QAction(tr("E&xit"), this);
    ActClose->setShortcuts(QKeySequence::Quit);
    ActClose->setStatusTip(tr("Exit the application"));
    connect(ActClose, SIGNAL(triggered()), this, SLOT(closeApp()));

    timer = new QTimer();
    timer->setInterval(250);
    connect(timer, SIGNAL(timeout()), this, SLOT(timerTimeout()));
    timer->start();
}

MainWindow::~MainWindow()
{
    saveSettings();
    delete ui;
}

void MainWindow::paintEvent(QPaintEvent*)
{
    QPainter p(this);
    int top = 0;
    if(!setup.bDecorator) top = dispCaption(&p,top);

    p.setBackgroundMode(Qt::TransparentMode);
    p.setRenderHint(QPainter::Antialiasing,false);
    p.setRenderHint(QPainter::TextAntialiasing,false);

    if(setup.bEnableDateTime) top = dispTime(&p,top);
    if(setup.bEnableCPUGraph) top = dispCPUUsage(&p,top);
    if(setup.bEnableGPUState) top = dispGPUTemp(&p,top);
    if(setup.bEnableDiskUsage) top = dispDiskUsage(&p,top);
    if(setup.bEnableDiskIO) top = dispDiskIOState(&p,top);
    if(setup.bEnableNetIO) top = dispNetIOState(&p,top);
    if(setup.bEnableProcList) top = dispProcessList(&p,top);

    setGeometry(this->geometry().left(),
                this->geometry().top(),
                this->geometry().width(),top);
}

void MainWindow::mousePressEvent(QMouseEvent* event)
{
    if(event->button() == Qt::LeftButton)
    {
        mMousePosition = QCursor::pos();
        mWindowPos = this->geometry();
    }
}

void MainWindow::mouseMoveEvent(QMouseEvent* event)
{
    if( event->buttons().testFlag(Qt::LeftButton) )
    {
        QPoint p = QCursor::pos();
        if(mMoving)
        {
            p = QCursor::pos() - mMousePosition + mWindowPos.topLeft();
            this->setGeometry(p.x(),p.y(),mWindowPos.width(),mWindowPos.height());
        }
        else
        {
            int dx = p.x() - mMousePosition.x();
            int dy = p.y() - mMousePosition.y();
            int dm = abs(dx) + abs(dy);
            if(dm > 10) mMoving = true;
        }
    }
}

void MainWindow::mouseReleaseEvent(QMouseEvent* event)
{
    if(event->button() == Qt::LeftButton)
    {
        mMoving = false;
    }
}

void MainWindow::contextMenuEvent(QContextMenuEvent *event)
 {
     QMenu menu(this);
     menu.setStyleSheet("background-color: rgb(255, 255, 255);");
     menu.addAction(ActSetup);
     menu.addAction(ActReload);
     menu.addAction(ActClose);
     menu.exec(event->globalPos());
 }

QString MainWindow::getFullSettingFileName(QString name)
{
    QString path = QDir::homePath();
    path.append(QDir::separator()).append(name);
    path = QDir::toNativeSeparators(path);
    return path;
}

QString MainWindow::ColorToString(QColor color,bool bAlpha)
{
    QString str("");
    uint c = color.rgba();
    if(bAlpha)
        str = str.sprintf("#%08x",(c&0xffffffff));
    else
        str = str.sprintf("#%06x",(c&0x00ffffff));
    return str;
}

QColor MainWindow::StringToColor(QString str)
{
    bool ok;
    int r,g,b,a;

    a = str.mid(1,2).toInt(&ok,16);
    r = str.mid(3,2).toInt(&ok,16);
    g = str.mid(5,2).toInt(&ok,16);
    b = str.mid(7,2).toInt(&ok,16);

    return QColor(r,g,b,a);
}

void MainWindow::closeApp()
{
    qApp->quit();
}

void MainWindow::showSetup()
{
    SetupWindow * sw = new SetupWindow();
    sw->show();
}

void MainWindow::loadSettings(void)
{
    QString path = getFullSettingFileName(".mymonrc");
    QSettings settings(path, QSettings::IniFormat);

    settings.setIniCodec("UTF-8");

    int left = settings.value(tr("geometry/left"), 0).toInt();
    int top = settings.value(tr("geometry/top"), 0).toInt();
    int width = settings.value(tr("geometry/width"), 200).toInt();
    int height = settings.value(tr("geometry/height"), 600).toInt();

    setup.iDiskUsageCheckInterval = settings.value(tr("Interval/DiskUsageCheckInterval"), 10000).toInt();
    setup.iCPUUsageCheckInterval = settings.value(tr("Interval/CPUUsageCheckInterval"), 1000).toInt();
    setup.iDiskIOStateCheckInterval = settings.value(tr("Interval/DiskIOStateCheckInterval"), 1000).toInt();
    setup.iNetIOStateCheckInterval = settings.value(tr("Interval/NetIOStateCheckInterval"), 1000).toInt();
    setup.iGPUTempCheckInterval = settings.value(tr("Interval/GPUTempCheckInterval"), 10000).toInt();
    setup.iDiskTempCheckInterval = settings.value(tr("Interval/DiskTempCheckInterval"), 10000).toInt();
    setup.iCoreTempCheckInterval = settings.value(tr("Interval/CoreTempCheckInterval"), 10000).toInt();
    setup.iProcListCheckInterval = settings.value(tr("Interval/ProcListCheckInterval"), 2000).toInt();

    setup.bStayOnTop = settings.value(tr("Main/StayOnTop"),true).toBool();
    setup.bStayOnBottom = settings.value(tr("Main/StayOnBottom"),false).toBool();
    setup.bDecorator = settings.value(tr("Main/Decorator"),false).toBool();
    setup.bTransparent = settings.value(tr("Main/Transparent"),true).toBool();
    setup.fOpacity = settings.value(tr("Main/Opacity"),0.75).toFloat();
    setup.clShadowColor = StringToColor(settings.value(tr("Main/ShadowColor"),"#ff010101").toString());

    setup.bEnableDateTime = settings.value(tr("Module/EnableDateTime"),true).toBool();
    setup.bEnableCPUGraph = settings.value(tr("Module/EnableCPUGraph"),true).toBool();
    setup.bEnableCPUTemp = settings.value(tr("Module/EnableCPUTemp"),true).toBool();
    setup.bEnableCPUFreq = settings.value(tr("Module/EnableCPUFreq"),true).toBool();
    setup.bEnableDiskUsage = settings.value(tr("Module/EnableDiskUsage"),true).toBool();
    setup.bEnableDiskIO = settings.value(tr("Module/EnableDiskIO"),true).toBool();
    setup.bEnableDiskTemp = settings.value(tr("Module/EnableDiskTemp"),true).toBool();
    setup.bEnableNetIO = settings.value(tr("Module/EnableNetIO"),true).toBool();
    setup.bEnableGPUState = settings.value(tr("Module/EnableGPUState"),true).toBool();
    setup.bEnableProcList = settings.value(tr("Module/EnableProcList"),false).toBool();

    setup.bCPUDrawBack = settings.value(tr("CPU/DrawBack"),true).toBool();
    setup.bCPUDrawBorder = settings.value(tr("CPU/DrawBorder"),true).toBool();
    setup.bCPUHTML = settings.value(tr("CPU/HTMLFormat"),false).toBool();
    setup.CPUFormat = settings.value(tr("CPU/Format"),defaultFormatCPU).toString();
    setup.CPUHeight = settings.value(tr("CPU/Height"),25).toInt();
    setup.CPUFont.fromString(settings.value(tr("CPU/Font"),QFont("Monospace",10).toString()).toString());
    setup.CPUBackColor = StringToColor(settings.value(tr("CPU/BackColor"),"#4f000000").toString());
    setup.CPUBorderColor = StringToColor(settings.value(tr("CPU/BorderColor"),"#ff00007f").toString());
    setup.CPUGraphColor = StringToColor(settings.value(tr("CPU/GraphColor"),"#ffff0000").toString());
    setup.CPUTextColor = StringToColor(settings.value(tr("CPU/TextColor"),"#ffffff00").toString());

    setup.bGPUDrawBack = settings.value(tr("GPU/DrawBack"),true).toBool();
    setup.bGPUDrawBorder = settings.value(tr("GPU/DrawBorder"),true).toBool();
    setup.bGPUHTML = settings.value(tr("GPU/HTMLFormat"),false).toBool();
    setup.GPUFormat = settings.value(tr("GPU/Format"),defaultFormatGPU).toString();
    setup.GPUHeight = settings.value(tr("GPU/Height"),25).toInt();
    setup.GPUFont.fromString(settings.value(tr("GPU/Font"),QFont("Monospace",10).toString()).toString());
    setup.GPUBackColor = StringToColor(settings.value(tr("GPU/BackColor"),"#4f000000").toString());
    setup.GPUBorderColor = StringToColor(settings.value(tr("GPU/BorderColor"),"#ff00007f").toString());
    setup.GPUGraphColor = StringToColor(settings.value(tr("GPU/GraphColor"),"#ffff0000").toString());
    setup.GPUTextColor = StringToColor(settings.value(tr("GPU/TextColor"),"#ffffff00").toString());

    setup.bDiskUsageDrawBack = settings.value(tr("DiskUsage/DrawBack"),true).toBool();
    setup.bDiskUsageDrawBorder = settings.value(tr("DiskUsage/DrawBorder"),true).toBool();
    setup.bDiskUsageHTML = settings.value(tr("DiskUsage/HTMLFormat"),false).toBool();
    setup.DiskUsageFormat = settings.value(tr("DiskUsage/Format"),defaultFormatDiskUsage).toString();
    setup.DiskUsageHeight = settings.value(tr("DiskUsage/Height"),25).toInt();
    setup.DiskUsageFont.fromString(settings.value(tr("DiskUsage/Font"),QFont("Monospace",10).toString()).toString());
    setup.DiskUsageBackColor = StringToColor(settings.value(tr("DiskUsage/BackColor"),"#4f000000").toString());
    setup.DiskUsageBorderColor = StringToColor(settings.value(tr("DiskUsage/BorderColor"),"#ff00007f").toString());
    setup.DiskUsageGraphColor = StringToColor(settings.value(tr("DiskUsage/GraphColor"),"#ffff0000").toString());
    setup.DiskUsageTextColor = StringToColor(settings.value(tr("DiskUsage/TextColor"),"#ffffff00").toString());

    setup.bDiskIODrawBack = settings.value(tr("DiskIO/DrawBack"),true).toBool();
    setup.bDiskIODrawBorder = settings.value(tr("DiskIO/DrawBorder"),true).toBool();
    setup.bDiskIOHTML = settings.value(tr("DiskIO/HTMLFormat"),false).toBool();
    setup.DiskIOFormat = settings.value(tr("DiskIO/Format"),defaultFormatDiskIO).toString();
    setup.DiskIOHeight = settings.value(tr("DiskIO/Height"),25).toInt();
    setup.DiskIOFont.fromString(settings.value(tr("DiskIO/Font"),QFont("Monospace",10).toString()).toString());
    setup.DiskIOBackColor = StringToColor(settings.value(tr("DiskIO/BackColor"),"#4f000000").toString());
    setup.DiskIOBorderColor = StringToColor(settings.value(tr("DiskIO/BorderColor"),"#ff00007f").toString());
    setup.DiskIOGraphColor = StringToColor(settings.value(tr("DiskIO/GraphColor"),"#ffff0000").toString());
    setup.DiskIOTextColor = StringToColor(settings.value(tr("DiskIO/TextColor"),"#ffffff00").toString());

    setup.bNetIODrawBack = settings.value(tr("NetIO/DrawBack"),true).toBool();
    setup.bNetIODrawBorder = settings.value(tr("NetIO/DrawBorder"),true).toBool();
    setup.bNetIOHTML = settings.value(tr("NetIO/HTMLFormat"),false).toBool();
    setup.NetIOFormat = settings.value(tr("NetIO/Format"),defaultFormatNetIO).toString();
    setup.NetIOHeight = settings.value(tr("NetIO/Height"),25).toInt();
    setup.NetIOFont.fromString(settings.value(tr("NetIO/Font"),QFont("Monospace",10).toString()).toString());
    setup.NetIOBackColor = StringToColor(settings.value(tr("NetIO/BackColor"),"#4f000000").toString());
    setup.NetIOBorderColor = StringToColor(settings.value(tr("NetIO/BorderColor"),"#ff00007f").toString());
    setup.NetIOGraphColor = StringToColor(settings.value(tr("NetIO/GraphColor"),"#ffff0000").toString());
    setup.NetIOTextColor = StringToColor(settings.value(tr("NetIO/TextColor"),"#ffffff00").toString());

    setup.bAnalog = settings.value(tr("DateTime/Analog"),true).toBool();
    setup.bDateTimeDrawBack = settings.value(tr("DateTime/DrawBack"),true).toBool();
    setup.bDateTimeDrawBorder = settings.value(tr("DateTime/DrawBorder"),true).toBool();
    setup.bDateTimeHTML = settings.value(tr("DateTime/HTMLFormat"),true).toBool();
    setup.DateTimeFormat = settings.value(tr("DateTime/Format"),defaultFormatDateTime).toString();
    setup.DateTimeHeight = settings.value(tr("DateTime/Height"),30).toInt();
    setup.DateTimeFont.fromString(settings.value(tr("DateTime/Font"),QFont("Sans Serif",24).toString()).toString());
    setup.DateTimeBackColor = StringToColor(settings.value(tr("DateTime/BackColor"),"#4f000000").toString());
    setup.DateTimeBorderColor = StringToColor(settings.value(tr("DateTime/BorderColor"),"#ff00007f").toString());
    setup.DateTimeGraphColor = StringToColor(settings.value(tr("DateTime/GraphColor"),"#ffff0000").toString());
    setup.DateTimeTextColor = StringToColor(settings.value(tr("DateTime/TextColor"),"#ffffff00").toString());

    setup.bProcListDrawBack = settings.value(tr("ProcList/DrawBack"),true).toBool();
    setup.bProcListDrawBorder = settings.value(tr("ProcList/DrawBorder"),true).toBool();
    setup.bProcListHTML = settings.value(tr("ProcList/HTMLFormat"),false).toBool();
    setup.ProcListFormat = settings.value(tr("ProcList/Format"),defaultFormatProcList).toString();
    setup.ProcListCount = settings.value(tr("ProcList/Count"),5).toInt();
    setup.ProcListHeight = settings.value(tr("ProcList/Height"),30).toInt();
    setup.ProcListFont.fromString(settings.value(tr("ProcList/Font"),QFont("Monospace",10).toString()).toString());
    setup.ProcListBackColor = StringToColor(settings.value(tr("ProcList/BackColor"),"#4f000000").toString());
    setup.ProcListBorderColor = StringToColor(settings.value(tr("ProcList/BorderColor"),"#ff00007f").toString());
    setup.ProcListGraphColor = StringToColor(settings.value(tr("ProcList/GraphColor"),"#ffff0000").toString());
    setup.ProcListTextColor = StringToColor(settings.value(tr("ProcList/TextColor"),"#ffffff00").toString());

    setGeometry(left,top,width,height);

    if(setup.bTransparent)
    {
        setAutoFillBackground(false);
        setStyleSheet("background:transparent;");
        setAttribute(Qt::WA_TranslucentBackground);
    }
    else
    {
        setStyleSheet("");
        setAttribute(Qt::WA_NoSystemBackground, false);
        setAttribute(Qt::WA_TranslucentBackground,false);
    }

    Qt::WindowFlags Flags;

    if(setup.bDecorator) Flags = Qt::Window;
    else                 Flags = Qt::FramelessWindowHint;

    if(setup.bStayOnTop) Flags |= Qt::WindowStaysOnTopHint;
    if(setup.bStayOnBottom) Flags |= Qt::WindowStaysOnBottomHint;

    setWindowFlags(Flags);

    this->setWindowOpacity(setup.fOpacity);

    this->show();

    MAX_DISPLAY = this->geometry().width();
}


void MainWindow::setSticky()
{
    /*
    Atom a = XInternAtom(this->x11Info().display(), "_NET_WM_DESKTOP", 1);
    if (a != None)
    {
        long prop = 0xFFFFFFFF;
        XChangeProperty(display, window, a, XA_CARDINAL, 32, PropModeAppend, (unsigned char *) &prop, 1);
    }

    a = XInternAtom(display, "_NET_WM_STATE", True);
    if (a != None)
    {
        Atom prop = XInternAtom(display, "_NET_WM_STATE_STICKY", True);
        XChangeProperty(display, window, a, XA_ATOM, 32, PropModeAppend, (unsigned char *) &prop, 1);
    }
*/
}

void MainWindow::skipTaskNPager()
{
/*
    Atom a = XInternAtom(display, "_NET_WM_STATE", True);
    if (a != None)
    {
        Atom prop = XInternAtom(display, "_NET_WM_STATE_SKIP_TASKBAR", True);
        XChangeProperty(display, window, a, XA_ATOM, 32, PropModeAppend, (unsigned char *) &prop, 1);
    }

    a = XInternAtom(display, "_NET_WM_STATE", True);
    if (a != None)
    {
        Atom prop = XInternAtom(display, "_NET_WM_STATE_SKIP_PAGER", True);
        XChangeProperty(display, window, a, XA_ATOM, 32, PropModeAppend, (unsigned char *) &prop, 1);
    }
    */
}

void MainWindow::bottomLayer()
{
    /*
    Atom a = XInternAtom(display, "_WIN_LAYER", True);
    if (a != None)
    {
        long prop = 0; // 6 is above && _NET_WM_STATE_ABOVE in STATE
        XChangeProperty(display, window, a, XA_CARDINAL, 32, PropModeAppend, (unsigned char *) &prop, 1);
    }

    a = XInternAtom(display, "_NET_WM_STATE", True);
    if (a != None)
    {
        Atom prop = XInternAtom(display, "_NET_WM_STATE_BELOW", True);
        XChangeProperty(display, window, a, XA_ATOM, 32, PropModeAppend, (unsigned char *) &prop, 1);
    }
    */
}

void MainWindow::saveSettings(void)
{
    QString path = getFullSettingFileName(".mymonrc");
    QSettings settings(path, QSettings::IniFormat);

    settings.setIniCodec("UTF-8");

    QRect rect = geometry();

    settings.setValue("geometry/left", rect.left());
    settings.setValue("geometry/top", rect.top());
    settings.setValue("geometry/width", rect.width());
    settings.setValue("geometry/height", rect.height());
}

QString MainWindow::RunCommand(QString cmd)
{
    QProcess prcs;

    prcs.setReadChannelMode(QProcess::MergedChannels);

    prcs.start(cmd);

    prcs.waitForFinished(250);

    QByteArray result = prcs.readAllStandardOutput();
    QString ret(result);

//    qDebug() << ret;
    return ret;
}

QString MainWindow::OpenStateFile(QString file)
{
    QFile f(file);
    if (!f.open(QIODevice::ReadOnly | QIODevice::Text))
        return QString("");
    QByteArray ba = f.readAll();
    f.close();
    QString ret = ba;
//    qDebug() << ret;
    return ret;
}

QString MainWindow::NetCat(QString ip,int port)
{
    QTcpSocket *sock = new QTcpSocket();
    sock->connectToHost(ip,port);
    sock->waitForDisconnected(500);
    QByteArray ba = sock->readAll();
    QString str = ba;
    str = str.replace("||","\n|");
    return str;
}

void MainWindow::timerTimeout()
{
    QString ret;
    bool bNeedUpdate = false;
    qint64 xx = QDateTime::currentMSecsSinceEpoch();
//    timer->setInterval();
    if(setup.bEnableDiskUsage && (msDiskUsageCheck < xx))
    {
//        ret = RunCommand("df -k");
        parseDiskUsage(ret);
        msDiskUsageCheck = msDiskUsageCheck + setup.iDiskUsageCheckInterval;
        bNeedUpdate = true;
    }
    if(setup.bEnableCPUGraph && (msCPUUsageCheck < xx))
    {
        ret = OpenStateFile("/proc/stat");
        parseCPUUsage(ret);
        msCPUUsageCheck = msCPUUsageCheck + setup.iCPUUsageCheckInterval;
        bNeedUpdate = true;
    }
    if(setup.bEnableDiskIO && (msDiskIOStateCheck < xx))
    {
        ret = OpenStateFile("/proc/diskstats");
        parseDiskIOState(ret);
        getDiskIOMax();
        msDiskIOStateCheck = msDiskIOStateCheck + setup.iDiskIOStateCheckInterval;
        bNeedUpdate = true;
    }
    if(setup.bEnableNetIO && (msNetIOStateCheck < xx))
    {
        ret = OpenStateFile("/proc/net/dev");
        parseNetIOState(ret);
        getNetIOMax();
        msNetIOStateCheck = msNetIOStateCheck + setup.iNetIOStateCheckInterval;
        bNeedUpdate = true;
    }
    if(setup.bEnableGPUState && (msGPUTempCheck < xx))
    {
//        ret = RunCommand("nvidia-settings -q gpucoretemp -t");
        ret = RunCommand("nvidia-smi -q -d MEMORY,TEMPERATURE");

        parseGPUTemp(ret);
        msGPUTempCheck = msGPUTempCheck + setup.iGPUTempCheckInterval;
        bNeedUpdate = true;
    }
    if(setup.bEnableDiskTemp && (msDiskTempCheck < xx))
    {
//        ret = RunCommand("netcat 127.0.0.1 7634");
        ret = NetCat("127.0.0.1",7634);
        parseDiskTemp(ret);
        msDiskTempCheck = msDiskTempCheck + setup.iDiskTempCheckInterval;
        bNeedUpdate = true;
    }
    if(setup.bEnableCPUTemp && (msCoreTempCheck < xx))
    {
//        ret = RunCommand("sensors");
        parseCoreTemp(ret);
        msCoreTempCheck = msCoreTempCheck + setup.iCoreTempCheckInterval;
        bNeedUpdate = true;
    }
    if(setup.bEnableProcList && (msProcListCheck < xx))
    {
//        ret = RunCommand("ps h -eo pcpu,pmem,pid,ruser,comm");
        parseProcessList(ret);
        msProcListCheck = msProcListCheck + setup.iProcListCheckInterval;
        bNeedUpdate = true;
    }

    if(bNeedUpdate) this->update();
}

void MainWindow::getDiskIOMax()
{
    int i,j;
    IOSTATE dio1,dio2;

    for(i = 0 ; i < DiskIOCount ; i++)
    {
        if(DiskIO[i].count() > 1)
        {
            dio1 = DiskIO[i].at(0);
            dio2 = DiskIO[i].at(1);

            DiskIOStateMax[i].dev = dio1.dev;
            DiskIOStateMax[i].r = dio1.r - dio2.r;
            DiskIOStateMax[i].w = dio1.w - dio2.w;

            for(j = 0; j < (DiskIO[i].count() - 1); j++)
            {
                dio1 = DiskIO[i].at(j);
                dio2 = DiskIO[i].at(j+1);
                if(DiskIOStateMax[i].r < (dio1.r - dio2.r))
                {
                    DiskIOStateMax[i].r = dio1.r - dio2.r;
                }
                if(DiskIOStateMax[i].w < (dio1.w - dio2.w))
                {
                    DiskIOStateMax[i].w = dio1.w - dio2.w;
                }
            }
        }
        else
        {
            DiskIOStateMax[i].dev = "";
            DiskIOStateMax[i].r = 1024;
            DiskIOStateMax[i].w = 1024;
        }
    }
}

void MainWindow::getNetIOMax()
{
    int i,j;
    IOSTATE nio1,nio2;

    for(i = 0 ; i < NetIOCount ; i++)
    {
        if(NetIO[i].count() > 1)
        {
            nio1 = NetIO[i].at(0);
            nio2 = NetIO[i].at(1);

            NetIOStateMax[i].dev = nio1.dev;
            NetIOStateMax[i].r = nio1.r - nio2.r;
            NetIOStateMax[i].w = nio1.w - nio2.w;

            for(j = 0; j < (NetIO[i].count() - 1); j++)
            {
                nio1 = NetIO[i].at(j);
                nio2 = NetIO[i].at(j+1);
                if(NetIOStateMax[i].r < (nio1.r - nio2.r))
                {
                    NetIOStateMax[i].r = nio1.r - nio2.r;
                }
                if(NetIOStateMax[i].w < (nio1.w - nio2.w))
                {
                    NetIOStateMax[i].w = nio1.w - nio2.w;
                }
            }
        }
        else
        {
            NetIOStateMax[i].dev = "";
            NetIOStateMax[i].r = 1024;
            NetIOStateMax[i].w = 1024;
        }
    }
}

QString MainWindow::getSizeString(qint64 size)
{
    QString ret;
    double d = size;
    const char *sizes[] = {"Kb", "Mb", "Gb", "Tb", "Pb", "Eb", "Zb", "Yb"};
    uint i = 0;
    while (d >= 1000) {
        d /= 1024.0;
        i++;
    }

    ret.sprintf("%5.1f%s",d,sizes[i]);
    return ret;
}

void MainWindow::parseDiskUsage(QString str)
{
/*
    Filesystem     1K-blocks      Used Available Use% Mounted on
    /dev/sda2       33474324  14615240  17181364  46% /
    udev             2054424         4   2054420   1% /dev
    tmpfs             824680      1288    823392   1% /run
    none                5120         4      5116   1% /run/lock
    none             2061696      1844   2059852   1% /run/shm
    /dev/sdb1      976759804 914949784  61810020  94% /media/01Game
    /dev/sdd1      976759804 929228936  47530868  96% /media/02Down
    /dev/sdh1      488382460 402076440  86306020  83% /media/03Temp
    /dev/sda5      513730556 450243832  63486724  88% /media/04Data
    /dev/sdf1      312214316 194943832 117270484  63% /home
    /dev/sdg1      488384000 422189632  66194368  87% /media/SAMSUNG
    /dev/sde1         251910        34    251876   1% /media/ECAA-4370
    /dev/sdc1        7810148         4   7810144   1% /media/4D36-E388
*/
/*
    int i;
    int diskcnt = 0;
    QStringList disks = str.split("\n");
    QString disk;

    for(i = 0 ; i < disks.count() ; i++)
    {
        disk = disks.at(i);
        if(disk.indexOf("/dev/") > -1)
        {
            while(disk.indexOf("  ") > -1) disk.replace("  "," ");
            QStringList sl = disk.split(" ");
            if(sl.count() > 5)
            {
                QString total = sl.at(1);
                QString avail = sl.at(3);
                DiskUsage[diskcnt].mount = sl.at(5);
                DiskUsage[diskcnt].dev = sl.at(0);
                DiskUsage[diskcnt].total = total.toLongLong();
                DiskUsage[diskcnt].avail = avail.toLongLong();
                DiskUsage[diskcnt].valid = true;
                diskcnt++;
            }
        }
    }
*/


    int diskcnt = 0, i;
    struct mntent *entry;
    struct statfs statistics;

    FILE *fsDescription = setmntent(_PATH_MOUNTED, "r");

    while ((entry = getmntent(fsDescription)) != NULL)
    {
        if (statfs(entry->mnt_dir, &statistics) == 0)
        {
//            qlonglong blockSize = statistics.f_bsize;
//            qlonglong availBlocks = statistics.f_bavail * blockSize;
//            qlonglong totalBlocks = statistics.f_blocks * blockSize;
            str = entry->mnt_fsname;
            if(str.indexOf("/dev/") > -1)
            {
                DiskUsage[diskcnt].mount = entry->mnt_dir;
                DiskUsage[diskcnt].dev = entry->mnt_fsname;
                DiskUsage[diskcnt].avail = statistics.f_bavail * (statistics.f_bsize / 1024.0);
                DiskUsage[diskcnt].total = statistics.f_blocks * (statistics.f_bsize / 1024.0);
                DiskUsage[diskcnt].valid = true;
                diskcnt++;
            }
        }
    }
    endmntent(fsDescription);

    for(i = diskcnt; i < MAX_DEVICE; i++) DiskUsage[i].valid = false;
    DiskUsageCount = diskcnt;
}

void MainWindow::parseCPUUsage(QString str)
{
    /*
cpu  7627710 158588 8501536 80863270 670205 215 120861 0 0 0
cpu0 3772826 78488 4378042 40264340 382211 62 66626 0 0 0
cpu1 3854883 80100 4123493 40598930 287994 152 54235 0 0 0
    */
    int i;
    int cpucnt = 0;
    QStringList cpus = str.split("\n");
    QString cpu;
    COREUSAGE Core;

    for(i = 0 ; i < cpus.count() ; i++)
    {
        cpu = cpus.at(i);
        if(cpu.indexOf("cpu") > -1)
        {
            while(cpu.indexOf("  ") > -1) cpu.replace("  "," ");
            QStringList sl = cpu.split(" ");
            if(sl.count()>7)
            {
                qint64 total = sl.at(1).toLongLong() +
                               sl.at(2).toLongLong() +
                               sl.at(3).toLongLong() +
                        sl.at(4).toLongLong() +
                        sl.at(5).toLongLong() +
                        sl.at(6).toLongLong() +
                        sl.at(7).toLongLong() ;
                qint64 idle = sl.at(4).toLongLong();
                Core.dev = sl.at(0);
                Core.total = total;
                Core.idle = idle;
                CoreUsage[cpucnt].insert(0,Core);
                while(CoreUsage[cpucnt].count() > MAX_DISPLAY) CoreUsage[cpucnt].removeLast();
                cpucnt++;
            }
        }
    }
    CoreCount = cpucnt;
}

void MainWindow::parseDiskIOState(QString str)
{
/*
   8       0 sda 622136 3910067 53808830 3006400 413930 238318 24458736 7146536 0 2658016 10152328
   8       1 sda1 166 0 1328 396 0 0 0 0 0 396 396
   8       2 sda2 469367 84599 22213266 2487564 375409 205251 24059072 7023244 0 2391576 9510984
   8       3 sda3 160 0 1280 460 0 0 0 0 0 460 460
   8       4 sda4 2 0 12 140 0 0 0 0 0 140 140
   8       5 sda5 140485 3824358 31488416 433148 855 2258 24904 45748 0 290984 478748
   8       6 sda6 11789 1110 103192 83596 16036 30809 374760 72164 0 28920 155724
   8      16 sdb 1954 63244 95816 7332 18 696 5712 68 0 6552 7400
   8      17 sdb1 1787 63244 94480 7276 18 696 5712 68 0 6496 7344
   8      32 sdc 16564 18156 59786 10164 155 15528 32265 315984 0 11384 326148
   8      33 sdc1 15964 15907 50426 8936 133 15400 31065 314636 0 10208 323572
   8      48 sdd 4902 165528 938008 7908 0 0 0 0 0 5700 7900
   8      49 sdd1 4735 165528 936672 7860 0 0 0 0 0 5652 7852
   8      64 sde 363 2118 3681 348 1 0 1 0 0 288 348
   8      65 sde1 350 2118 3577 332 1 0 1 0 0 272 332
   8      80 sdf 460907 107640 47352600 2304436 560849 466847 103562458 46618904 0 4036092 48925824
   8      81 sdf1 460876 107640 47352352 2304384 545213 466847 103562458 46287412 0 3728796 48593976
   8      96 sdg 324595 4422058 37749945 6446332 29454 732947 6099208 13806736 0 2762384 20252824
   8      97 sdg1 324565 4422058 37749705 6445864 29454 732947 6099208 13806736 0 2761916 20252356
   8     112 sdh 4753 50114 220256 29700 92 179 2168 232 0 27752 29912
   8     113 sdh1 4586 50114 218920 29532 92 179 2168 232 0 27588 29748
*/
    bool flag;
    int i, k;
    int ioidx = 0;
    QStringList diskios = str.split("\n");
    QString diskio;
    IOSTATE io,dio;

    for(i = 0 ; i < diskios.count() ; i++)
    {
        diskio = diskios.at(i);
        diskio = diskio.trimmed();
        while(diskio.indexOf("  ") > -1) diskio.replace("  "," ");
        QStringList sl = diskio.split(" ");

        if((sl.at(0).toInt() == 8) && (sl.at(2).length() == 3))
        {
            io.dev = sl.at(2);
            io.r = sl.at(5).toLongLong() / 2;
            io.w = sl.at(9).toLongLong() / 2;
            flag = true;
            for(k = 0 ; k < DiskIOCount ; k++)
            {
                if(DiskIO[k].count() > 0)
                {
                    dio = DiskIO[k].at(0);
                    if(dio.dev == io.dev)
                    {
                        ioidx = k;
                        flag = false;
                    }
                }
            }
            if(flag)
            {
                ioidx = DiskIOCount;
                DiskIOCount++;
            }
            DiskIO[ioidx].insert(0,io);
            while(DiskIO[ioidx].count() > MAX_DISPLAY) DiskIO[ioidx].removeLast();
        }
    }
}

void MainWindow::parseNetIOState(QString str)
{
/*
Inter-|   Receive                                                |  Transmit
 face |bytes    packets errs drop fifo frame compressed multicast|bytes    packets errs drop fifo colls carrier compressed
    lo: 619371896 4254827    0    0    0     0          0         0 619371896 4254827    0    0    0     0       0          0
vmnet8:       0    5285    0    0    0     0          0         0        0    1415    0    0    0     0       0          0
  eth1: 57678216  920742    0    0    0     0          0         0   470737    2423    0    0    0     0       0          0
 wlan0: 21460895  114465    0    0    0     0          0         0  1582893    4596    0    0    0     0       0          0
  eth0: 2544839404 22099140    0    0    0     0          0         0 876026511 12436226    0    0    0     0       0          0
vmnet1:       0       0    0    0    0     0          0         0        0    1415    0    0    0     0       0          0
*/
    bool flag;
    int i,k;
    int ioidx = 0;
    QStringList netios = str.split("\n");
    QString netio;
    IOSTATE io,nio;

    netios.sort();

    for(i = 0 ; i < netios.count() ; i++)
    {
        netio = netios.at(i);
        netio = netio.trimmed();
        while(netio.indexOf("  ") > -1) netio.replace("  "," ");
        QStringList sl = netio.split(" ");

        if( (sl.at(0).indexOf("eth") >= 0) || (sl.at(0).indexOf("wlan") >= 0) )
        {
            io.dev = sl.at(0);
            io.r = sl.at(1).toLongLong() / 1024;
            io.w = sl.at(9).toLongLong() / 1024;
            flag = true;
            for(k = 0 ; k < NetIOCount ; k++)
            {
                if(NetIO[k].count() > 0)
                {
                    nio = NetIO[k].at(0);
                    if(nio.dev == io.dev)
                    {
                        ioidx = k;
                        flag = false;
                    }
                }
            }
            if(flag)
            {
                ioidx = NetIOCount;
                NetIOCount++;
            }
            NetIO[ioidx].insert(0,io);
            while(NetIO[ioidx].count() > MAX_DISPLAY) NetIO[ioidx].removeLast();
        }
    }
}

void MainWindow::parseGPUTemp(QString str)
{
/*
//
ret = RunCommand("nvidia-smi -q -d MEMORY,TEMPERATURE");
==============NVSMI LOG==============

Timestamp                       : Sun Jun 24 06:01:23 2012

Driver Version                  : 295.40

Attached GPUs                   : 1

GPU 0000:01:00.0
    Memory Usage
        Total                   : 511 MB
        Used                    : 236 MB
        Free                    : 275 MB
    Temperature
        Gpu                     : 50 C
*/
    int i;
    while(str.indexOf(" ") > -1) str = str.replace(" ","");
    QStringList infos = str.split("\n");
    QString info;
    for(i = 0 ; i < infos.count() ; i++)
    {
        info = infos.at(i);
        if(info.length() > 5)
        {
            QStringList sl = info.split(":");
            if(sl.count() > 1)
            {
                if(sl.at(0).indexOf("DriverVersion") > -1) gpuTemp.ver = sl.at(1);
                if(sl.at(0).indexOf("Gpu") > -1) gpuTemp.temp = sl.at(1);
                if(sl.at(0).indexOf("Total") > -1) gpuTemp.tmem = sl.at(1);
                if(sl.at(0).indexOf("Free") > -1) gpuTemp.fmem = sl.at(1);
            }
        }
    }

/*
ret = RunCommand("nvidia-settings -q gpucoretemp -t");
    DEVTEMP t;
    for(int i = 0 ; i < DevTemp.count() ; i++)
    {
        t = DevTemp.at(i);
        if(t.dev == "GPU")
        {
            t.temp = "+"+str+"°C";
            DevTemp.replace(i,t);
            return;
        }
    }
    t.dev = "GPU";
    t.temp = "+"+str+"°C";
    DevTemp.insert(0,t);
*/
}

void MainWindow::parseDiskTemp(QString str)
{
/*
|/dev/sda|WDC WD6400AAKS-00A7B0|33|C
|/dev/sdb|WDC WD10EALX-009BA0|37|C
|/dev/sdd|WDC WD10EALX-009BA0|34|C
|/dev/sdf|WDC WD3200SD-01KNB0|33|C
|/dev/sdh|ST3500320AS|30|C|
*/
    str = str.replace("||","\n|");
    int i,k;
    QStringList temps = str.split("\n");
    QString temp;
    DEVTEMP d,t;
    bool flag;
/*
    for(i = 0 ; i < DevTemp.count() ; i++)
    {
        d = DevTemp.at(i);
        if(d.dev.indexOf("/dev/sd") > -1)
        {
            d.temp = "";
            DevTemp.replace(i,d);
        }
    }
*/
    for(i = 0 ; i < temps.count() ; i++)
    {
        temp = temps.at(i);

        QStringList sl = temp.split("|");
        if(sl.count() > 3)
        {
            if(sl.at(1).indexOf("dev/") > 0)
            {
                d.dev = sl.at(1);
                d.temp = sl.at(3)+"C";
                flag = true;
                for(k = 0 ; k < DevTemp.count() ; k++)
                {
                    t = DevTemp.at(k);
                    if(t.dev == d.dev)
                    {
                        flag = false;
                        DevTemp.replace(k,d);
                    }
                }
                if(flag) DevTemp.insert(0,d);
            }
        }
    }
}

void MainWindow::parseCoreTemp(QString str)
{
/*coretemp-isa-0000
Adapter: ISA adapter
Core 0:       +41.0°C  (high = +80.0°C, crit = +100.0°C)
Core 1:       +40.0°C  (high = +80.0°C, crit = +100.0°C)

it8718-isa-0290
Adapter: ISA adapter
in0:          +0.98 V  (min =  +0.00 V, max =  +4.08 V)
in1:          +1.90 V  (min =  +0.00 V, max =  +4.08 V)

    int i,k;
    QStringList cputemps = str.split("\n");
    QString cputemp;
    DEVTEMP d,t;
    bool flag;

    for(i = 0 ; i < cputemps.count() ; i++)
    {
        cputemp = cputemps.at(i);

        while(cputemp.indexOf("  ") > -1) cputemp.replace("  "," ");
        cputemp = cputemp.replace("Core ","cpu").replace(":","");
        QStringList sl = cputemp.split(" ");
        if(sl.count() > 2)
        {
            d.dev = sl.at(0);
            d.temp = sl.at(1);
            if(sl.at(0).left(3) == "cpu")
            {
                d.temp = d.temp.left(5);
                flag = true;
                for(k = 0 ; k < DevTemp.count() ; k++)
                {
                    t = DevTemp.at(k);
                    if(t.dev == d.dev)
                    {
                        flag = false;
                        DevTemp.replace(k,d);
                    }
                }
                if(flag) DevTemp.insert(0,d);
            }
        }
    }

*/

/*
/sys/devices/platform/coretemp.0

driver@   power/            temp2_input  temp3_crit_alarm  uevent
hwmon/    subsystem@        temp2_label  temp3_input
modalias  temp2_crit        temp2_max    temp3_label
name      temp2_crit_alarm  temp3_crit   temp3_max
*/

    QFile file;
    DEVTEMP d,t;
    bool flag;
    char label[32];
    char input[32];
    for(int i = 0 ; i < MAX_CORE ; i++)
    {
        file.setFileName("/sys/devices/platform/coretemp.0/temp"+QString::number(i)+"_label");

        if(file.open(QIODevice::ReadOnly | QIODevice::Text))
        {
            bzero(label,32);
            file.readLine(label,32);
            file.close();

            file.setFileName("/sys/devices/platform/coretemp.0/temp"+QString::number(i)+"_input");
            file.open(QIODevice::ReadOnly | QIODevice::Text);
            bzero(input,32);
            file.readLine(input,32);
            file.close();

            d.dev = label;
            if(d.dev.left(4) == QString("Core"))
            {
                d.dev = d.dev.replace("Core ","cpu");
                d.dev = d.dev.left(4);
                str = input;
                d.temp = QString("%1C").arg(str.toLong() / 1000.0);
                flag = true;
                for(int k = 0 ; k < DevTemp.count() ; k++)
                {
                    t = DevTemp.at(k);
                    if(t.dev == d.dev)
                    {
                        flag = false;
                        DevTemp.replace(k,d);
                    }
                }
                if(flag) DevTemp.insert(0,d);
            }
        }
    }
}

void MainWindow::parseProcessList(QString str)
{
    bool ok;
    int pid;
    int proccnt = 0;
    qlonglong cpu,mem;
    QFile file;
    PROCLIST t;
    QString name;
    QStringList data;
    float dcpu = 0;
    int valid = qrand();

    file.setFileName("/proc/stat");
    if(file.open(QIODevice::ReadOnly))
    {
        data = QString::fromLocal8Bit(file.readLine()).replace("  "," ").split(' ');
        file.close();
        cpu =   data.at(1).toLongLong() +
                data.at(2).toLongLong() +
                data.at(3).toLongLong() +
                data.at(4).toLongLong();

        t = ProcMap[0];
        t.pcpu = t.cpu;
        t.cpu = cpu;
        t.valid = valid;
        ProcMap[0] = t;
        dcpu = t.cpu - t.pcpu;
    }

    QStringList procnames = QDir("/proc/").entryList();
    procnames.sort();

    ProcList.clear();
    if (procnames.isEmpty())
        return;

    foreach (const QString &procname, procnames)
    {
        procname.toInt(&ok);
        if(ok)
        {
            file.setFileName("/proc/" + procname + "/stat");
            if(file.open(QIODevice::ReadOnly))
            {
                data = QString::fromLocal8Bit(file.readAll()).split(' ');
                file.close();
                name = data.at(1);
                name = name.replace("(","",Qt::CaseSensitive).replace(")","",Qt::CaseSensitive);
                cpu = data.at(13).toLongLong() + data.at(14).toLongLong();
                mem = data.at(22).toLongLong() + data.at(23).toLongLong();
                pid = procname.toInt();

                t = ProcMap[pid];
                t.valid = valid;
                t.pcpu = t.cpu;
                t.pmem = t.mem;
                t.cpu = cpu;
                t.mem = mem;
                if(dcpu > 0)  t.cper = (float)(100.0 * (t.cpu - t.pcpu)) / dcpu;
                else          t.cper = 0;
                ProcMap[pid] = t;

                name = str.sprintf("%4.1f%% %6d",t.cper*2.0,pid) + " " + name;
                ProcList.insert(0,name);
                proccnt ++;
            }
        }
    }

    ProcessCount = proccnt;
}

int MainWindow::dispTime(QPainter *p,int top)
{
    QRect rc;
    int w = this->geometry().width() - 1;
    rc = QRect(0,top,w,setup.DateTimeHeight);

    if(setup.bDateTimeDrawBack)   p->setBrush(QBrush(setup.DateTimeBackColor));
    else                          p->setBrush(Qt::NoBrush);
    if(setup.bDateTimeDrawBorder) p->setPen(setup.DateTimeBorderColor);
    else                          p->setPen(Qt::NoPen);

    if(setup.bDateTimeDrawBack || setup.bDateTimeDrawBorder)
        p->drawRoundedRect(rc,5,5);

    if(setup.bAnalog)
    {
        static const QPoint hourHand[4] = {
            QPoint(7, 0),
            QPoint(0,8),
            QPoint(-7, 0),
            QPoint(0, -40)
        };
        static const QPoint minuteHand[4] = {
            QPoint(5, 0),
            QPoint(0,8),
            QPoint(-5, 0),
            QPoint(0, -70)
        };
        static const QPoint secondHand[4] = {
            QPoint(3, 0),
            QPoint(0,8),
            QPoint(-3, 0),
            QPoint(0, -90)
        };
        QColor hourColor(127, 0, 127,255);
        QColor minuteColor(0, 127, 127, 210);
        QColor secondColor(255, 63, 63, 180);

        int side = qMin(rc.width(), rc.height());

        QTime time = QTime::currentTime();

        p->setRenderHint(QPainter::Antialiasing,true);
        p->translate(rc.width() / 2, rc.height() / 2 + rc.top());

        p->scale(side / 200.0, side / 200.0);


        p->setPen(Qt::NoPen);
        p->setBrush(hourColor);

        p->save();
        p->rotate(30.0 * ((time.hour() + time.minute() / 60.0)));
        p->drawConvexPolygon(hourHand, 4);
        p->restore();

        p->setPen(hourColor);

        for (int i = 0; i < 12; ++i) {
            p->drawLine(88, 0, 96, 0);
            p->rotate(30.0);
        }

        p->setPen(Qt::NoPen);
        p->setBrush(minuteColor);

        p->save();
        p->rotate(6.0 * (time.minute() + time.second() / 60.0));
        p->drawConvexPolygon(minuteHand, 4);
        p->restore();

        p->setPen(minuteColor);
        for (int j = 0; j < 60; ++j) {
            if ((j % 5) != 0)
                p->drawLine(92, 0, 96, 0);
            p->rotate(6.0);
        }

        p->setPen(Qt::NoPen);
        p->setBrush(secondColor);

        p->save();
        p->rotate(6.0 * time.second() );
        p->drawConvexPolygon(secondHand, 4);
        p->restore();

        p->setRenderHint(QPainter::Antialiasing,false);
        p->scale(200.0/side,200.0/side);
        p->translate(-(rc.width() / 2), -(rc.height() / 2 + rc.top()));

    }
    else
    {

        QString disp;
        disp = setup.DateTimeFormat;

        QDateTime dtm = QDateTime::currentDateTime();

        disp = disp.replace("$year",QString::number(dtm.date().year()));
        disp = disp.replace("$month",QString::number(dtm.date().month()).rightJustified(2, '0'));
        disp = disp.replace("$day",QString::number(dtm.date().day()).rightJustified(2, '0'));
        disp = disp.replace("$hour",QString::number(dtm.time().hour()).rightJustified(2, '0'));
        disp = disp.replace("$minute",QString::number(dtm.time().minute()).rightJustified(2, '0'));
        disp = disp.replace("$second",QString::number(dtm.time().second()).rightJustified(2, '0'));

        if(setup.bDateTimeHTML)
        {

            disp = disp.replace("$CRLF","<br>");
            QString head = QString(htmlHEAD).arg(setup.DateTimeFont.family())
                    .arg(ColorToString(setup.DateTimeTextColor,false))
                    .arg(setup.DateTimeFont.pointSizeF())
                    .arg(setup.DateTimeFont.weight() * 8)
                    .arg((setup.DateTimeFont.italic() ? "italic" : "normal"));

            disp = head + disp + htmlTAIL;

            QTextDocument doc;
            doc.setUseDesignMetrics(true);
            doc.setHtml(disp);
            doc.setPageSize(rc.size());

            p->save();
            QRectF clip(0, 0, rc.width(), rc.height());
            p->translate(rc.left(), rc.top());
            doc.drawContents(p,clip);
            p->restore();
        }
        else
        {
            disp = disp.replace("$CRLF","\n");
            p->setFont(setup.DateTimeFont);
            p->setPen(setup.clShadowColor);
            rc.adjust(5,2,-5,-2);

            int F = Qt::AlignVCenter | Qt::AlignCenter;
            rc.adjust(1,1,1,1);
            p->drawText(rc,F,disp);
            rc.adjust(-2,-2,-2,-2);
            p->drawText(rc,F,disp);
            p->setPen(setup.DateTimeTextColor);
            rc.adjust(1,1,1,1);
            p->drawText(rc,F,disp);
        }
    }
    top = top + setup.DateTimeHeight + 1;
    return top;
}

int MainWindow::dispCPUUsage(QPainter *p,int top)
{
    int i, j, k;
    int x,y,h;
    COREUSAGE cu1,cu2;
    QString disp;

    for(j = 0; j < CoreCount; j++)
    {
        QRect rc = QRect(0,top,this->geometry().width()-1,setup.CPUHeight);

        if(setup.bCPUDrawBack)   p->setBrush(QBrush(setup.CPUBackColor));
        else                     p->setBrush(Qt::NoBrush);
        if(setup.bCPUDrawBorder) p->setPen(setup.CPUBorderColor);
        else                     p->setPen(Qt::NoPen);
        if(setup.bCPUDrawBack || setup.bCPUDrawBorder)
            p->drawRoundedRect(rc,3,3);

        p->setPen(setup.CPUGraphColor);

        if(CoreUsage[j].count() > 1)
        {

            QString pcpu(""),temp(""),freq(""),dev("");
            cu1 = CoreUsage[j].at(0);
            cu2 = CoreUsage[j].at(1);
            x = rc.right() - i - 1;
            y = rc.bottom()-1;
            h = 100 - (((cu1.idle - cu2.idle) * 100.0) / (cu1.total - cu2.total));

            dev = cu1.dev;
            pcpu = pcpu.sprintf("%3d",h) + "%";

            if(dev.length() == 4)
            {
                if(setup.bEnableCPUFreq)
                {
                    freq = OpenStateFile("/sys/devices/system/cpu/"+dev+"/cpufreq/scaling_cur_freq");
                    freq.sprintf("%4.2fGHz",freq.toFloat() / 1000000.0);
                }
                if(setup.bEnableCPUTemp)
                for(k = 0 ; k < DevTemp.count() ; k++)
                    if(DevTemp.at(k).dev == dev)
                        temp = DevTemp.at(k).temp;
            }

            for(i = 0; i < rc.width() - 1;i++)
            {
                if (CoreUsage[j].count() > (i+1))
                {
                    cu1 = CoreUsage[j].at(i);
                    cu2 = CoreUsage[j].at(i+1);
                    x = rc.right() - i;
                    y = rc.bottom();
                    h = 100 - (((cu1.idle - cu2.idle) * 100.0) / (cu1.total - cu2.total));
                    h = (h * (setup.CPUHeight - 2)) / 100.0;
                    p->drawLine(x,y,x,y-h);
                }
            }

            disp = setup.CPUFormat;
            disp = disp.replace("$dev",dev);
            disp = disp.replace("$pcpu",pcpu);
            disp = disp.replace("$freq",freq);
            disp = disp.replace("$temp",temp);

            if(setup.bCPUHTML)
            {
                disp = disp.replace("$CRLF","<br>");
                QString head = QString(htmlHEAD).arg(setup.CPUFont.family())
                        .arg(ColorToString(setup.CPUTextColor,false))
                        .arg(setup.CPUFont.pointSizeF())
                        .arg(setup.CPUFont.weight() * 8)
                        .arg((setup.CPUFont.italic() ? "italic" : "normal"));

                disp = head + disp + htmlTAIL;
                qDebug() << disp;
                QTextDocument doc;
                doc.setPageSize(QSize(rc.width(),rc.height()));
                doc.setHtml(disp);
                doc.drawContents(p,rc);
            }
            else
            {
                disp = disp.replace("$CRLF","\n");

                p->setFont(setup.CPUFont);
                p->setPen(setup.clShadowColor);
                rc.adjust(5,2,-5,-2);

                int F = Qt::AlignLeft | Qt::AlignTop;
                rc.adjust(1,1,1,1);
                p->drawText(rc,F,disp);
                rc.adjust(-2,-2,-2,-2);
                p->drawText(rc,F,disp);
                p->setPen(setup.CPUTextColor);
                rc.adjust(1,1,1,1);
                p->drawText(rc,F,disp);
            }
            top = top + setup.CPUHeight + 1;
        }
    }
    return top;
}

int MainWindow::dispDiskUsage(QPainter *p,int top)
{
    int i;
    QString disp;
    QString mnt;
    QString dev;
    QRect rc;
    int w = this->geometry().width();
    for(i = 0; i < DiskUsageCount; i++)
    {
        double d = DiskUsage[i].per();
        rc = QRect(0,top,w,setup.DiskUsageHeight);

        if(setup.bDiskUsageDrawBack)   p->setBrush(QBrush(setup.DiskUsageBackColor));
        else                           p->setBrush(Qt::NoBrush);
        if(setup.bDiskUsageDrawBorder) p->setPen(setup.DiskUsageBorderColor);
        else                           p->setPen(Qt::NoPen);
        if(setup.bDiskUsageDrawBack || setup.bDiskUsageDrawBorder)
            p->drawRoundedRect(rc,5,5);

        p->setBrush(QBrush(setup.DiskUsageGraphColor));
        p->drawRoundedRect(QRect(0, top, w*d, setup.DiskUsageHeight),5,5);

        //  %free/%total %shortmount
        dev = DiskUsage[i].dev;
        mnt = DiskUsage[i].mount;
        disp = setup.DiskUsageFormat;
        disp = disp.replace("$free",getSizeString(DiskUsage[i].avail));
        disp = disp.replace("$total",getSizeString(DiskUsage[i].total));
        disp = disp.replace("$dev",dev);
        disp = disp.replace("$mount",mnt);
        dev = dev.replace("/dev/","");
        mnt = mnt.replace("/media/","");
        disp = disp.replace("$shortdev",dev);
        disp = disp.replace("$shortmount",mnt);

        if(setup.bDiskUsageHTML)
        {
            disp = disp.replace("$CRLF","<br>");
            QString head = QString(htmlHEAD).arg(setup.DiskUsageFont.family())
                    .arg(ColorToString(setup.DiskUsageTextColor,false))
                    .arg(setup.DiskUsageFont.pointSizeF())
                    .arg(setup.DiskUsageFont.weight() * 8)
                    .arg((setup.DiskUsageFont.italic() ? "italic" : "normal"));

            disp = head + disp + htmlTAIL;
            QTextDocument doc;
            doc.setPageSize(QSize(rc.width(),rc.height()));
            doc.setHtml(disp);
            doc.drawContents(p,rc);
        }
        else
        {
            disp = disp.replace("$CRLF","\n");

            p->setFont(setup.DiskUsageFont);
            p->setPen(setup.clShadowColor);
            rc.adjust(5,2,-5,-2);

            int F = Qt::AlignLeft | Qt::AlignTop;
            rc.adjust(1,1,1,1);
            p->drawText(rc,F,disp);
            rc.adjust(-2,-2,-2,-2);
            p->drawText(rc,F,disp);
            p->setPen(setup.DiskUsageTextColor);
            rc.adjust(1,1,1,1);
            p->drawText(rc,F,disp);
        }
        top = top + setup.DiskUsageHeight + 1;
    }
    return top;
}

int MainWindow::dispDiskIOState(QPainter *p,int top)
{
    int i, j;
    int x,y;
    float rh,wh,mh;
    IOSTATE dio1,dio2;
    QString disp,s,dtemp;

    for(j = 0; j < DiskIOCount; j++)
    {
        QRect rc = QRect(0,top,this->geometry().width()-1,setup.DiskIOHeight);

        if(setup.bDiskIODrawBack)   p->setBrush(QBrush(setup.DiskIOBackColor));
        else                        p->setBrush(Qt::NoBrush);
        if(setup.bDiskIODrawBorder) p->setPen(setup.DiskIOBorderColor);
        else                        p->setPen(Qt::NoPen);
        if(setup.bDiskIODrawBack || setup.bDiskIODrawBorder)
            p->drawRoundedRect(rc,3,3);

        if(DiskIO[j].count() > 1)
        {
            mh = 1024 * 1024 * 1024;
            dtemp = QString("n/a");
            for(i = 0; i < DevTemp.count(); i++)
            {
                s = DevTemp.at(i).dev;
                s.replace("/dev/","");
                if(s == DiskIO[j].at(0).dev)
                {
                    dtemp = DevTemp.at(i).temp;
                }
            }

            for(i = 0; i < DiskIOCount; i++)
            {
                if(DiskIO[j].at(0).dev == DiskIOStateMax[i].dev)
                {
                    mh = getDispMaxIO(DiskIOStateMax[i].r + DiskIOStateMax[i].w);
                }
            }

            dio1 = DiskIO[j].at(0);
            dio2 = DiskIO[j].at(1);

            disp = setup.DiskIOFormat;

            disp = disp.replace("$shortdev",dio1.dev);
            disp = disp.replace("$dev",dio1.dev);
            disp = disp.replace("$max",getSizeString(mh));
            disp = disp.replace("$temp",dtemp);
            disp = disp.replace("$R",getSizeString( dio1.r - dio2.r ));
            disp = disp.replace("$W",getSizeString( dio1.w - dio2.w ));
            disp = disp.replace("$TR",getSizeString(dio1.r));
            disp = disp.replace("$TW",getSizeString(dio1.w));

            for(i = 0; i < rc.width();i++)
            {
                if (DiskIO[j].count() > (i+1))
                {
                    dio1 = DiskIO[j].at(i);
                    dio2 = DiskIO[j].at(i+1);
                    x = rc.right() - i;
                    y = rc.bottom();
                    rh = ((dio1.r - dio2.r) * (setup.DiskIOHeight-2)) / mh;
                    wh = ((dio1.w - dio2.w) * (setup.DiskIOHeight-2)) / mh;

                    p->setPen(setup.DiskIOGraphColor);
                    p->drawLine(x,y,x,y-rh);
                    p->drawLine(x,y-rh,x,y-wh-rh);
                }
            }

            if(setup.bDiskIOHTML)
            {
                disp = disp.replace("$CRLF","<br>");
                QString head = QString(htmlHEAD).arg(setup.DiskIOFont.family())
                        .arg(ColorToString(setup.DiskIOTextColor,false))
                        .arg(setup.DiskIOFont.pointSizeF())
                        .arg(setup.DiskIOFont.weight() * 8)
                        .arg((setup.DiskIOFont.italic() ? "italic" : "normal"));

                disp = head + disp + htmlTAIL;
                QTextDocument doc;
                doc.setPageSize(QSize(rc.width(),rc.height()));
                doc.setHtml(disp);
                doc.drawContents(p,rc);
            }
            else
            {
                disp = disp.replace("$CRLF","\n");
                p->setFont(setup.DiskIOFont);
                p->setPen(setup.clShadowColor);
                rc.adjust(5,2,-5,-2);

                int F = Qt::AlignLeft | Qt::AlignVCenter;
                rc.adjust(1,1,1,1);
                p->drawText(rc,F,disp);
                rc.adjust(-2,-2,-2,-2);
                p->drawText(rc,F,disp);
                p->setPen(setup.DiskIOTextColor);
                rc.adjust(1,1,1,1);
                p->drawText(rc,F,disp);
            }

            top = top + setup.DiskIOHeight + 1;
        }
    }
    return top;
}

qint64 MainWindow::getDispMaxIO(qint64 io)
{
    int i = 0;
    qint64 x = 1024;
    qint64 a[] = {1        ,2        ,5,          //K Byte
                  10       ,20       ,50,         //K Byte
                  100      ,200      ,500,        //K Byte
                  1*x      ,2*x      ,5*x,        //M Byte
                  10*x     ,20*x     ,50*x,       //M Byte
                  100*x    ,200*x    ,500*x,      //M Byte
                  1*x*x    ,2*x*x    ,5*x*x,      //G Byte
                  10*x*x   ,20*x*x   ,50*x*x,     //G Byte
                  100*x*x  ,200*x*x  ,500*x*x,    //G Byte
                  1*x*x*x  ,2*x*x*x  ,5*x*x*x,    //T Byte
                  10*x*x*x ,20*x*x*x ,50*x*x*x,   //T Byte
                  100*x*x*x,200*x*x*x,500*x*x*x}; //T Byte
    while(io > a[i]) i++;
    return a[i];
}

int MainWindow::dispNetIOState(QPainter *p,int top)
{
    int i, j;
    int x,y,rh,wh,mh;
    IOSTATE nio1,nio2;
    QString disp;

    for(j = 0; j < NetIOCount; j++)
    {
        QRect rc = QRect(0,top,this->geometry().width()-1,setup.NetIOHeight);

        if(setup.bNetIODrawBack)   p->setBrush(QBrush(setup.NetIOBackColor));
        else                        p->setBrush(Qt::NoBrush);
        if(setup.bNetIODrawBorder) p->setPen(setup.NetIOBorderColor);
        else                        p->setPen(Qt::NoPen);
        if(setup.bNetIODrawBack || setup.bNetIODrawBorder)
            p->drawRoundedRect(rc,3,3);

        if(NetIO[j].count() > 1)
        {
            mh = 1024 * 1024 * 1024;
            for(i = 0; i < NetIOCount; i++)
            {
                if(NetIO[j].at(0).dev == NetIOStateMax[i].dev)
                {
                    mh = getDispMaxIO(NetIOStateMax[i].r + NetIOStateMax[i].w);
                }
            }

            nio1 = NetIO[j].at(0);
            nio2 = NetIO[j].at(1);

            disp = nio1.dev + " - "+getSizeString(mh)+"\n" +
                    "r : " + getSizeString( nio1.r - nio2.r ) + " " +
                    "w : " + getSizeString( nio1.w - nio2.w );

            for(i = 0; i < rc.width();i++)
            {
                if (NetIO[j].count() > (i+1))
                {
                    nio1 = NetIO[j].at(i);
                    nio2 = NetIO[j].at(i+1);
                    x = rc.right() - i;
                    y = rc.bottom();
                    rh = ((nio1.r - nio2.r) * (setup.NetIOHeight - 2)) / mh;
                    wh = ((nio1.w - nio2.w) * (setup.NetIOHeight - 2)) / mh;
                    p->setPen(setup.NetIOGraphColor);
                    p->drawLine(x,y,x,y-rh);
                    p->setPen(setup.NetIOGraphColor);
                    p->drawLine(x,y-rh,x,y-wh-rh);
                }
            }

            disp = setup.NetIOFormat;

            disp = disp.replace("$shortdev",nio1.dev);
            disp = disp.replace("$dev",nio1.dev);
            disp = disp.replace("$max",getSizeString(mh));
            disp = disp.replace("$R",getSizeString( nio1.r - nio2.r ));
            disp = disp.replace("$W",getSizeString( nio1.w - nio2.w ));
            disp = disp.replace("$TR",getSizeString(nio1.r));
            disp = disp.replace("$TW",getSizeString(nio1.w));

            if(setup.bNetIOHTML)
            {
                disp = disp.replace("$CRLF","<br>");
                QString head = QString(htmlHEAD).arg(setup.NetIOFont.family())
                        .arg(ColorToString(setup.NetIOTextColor,false))
                        .arg(setup.NetIOFont.pointSizeF())
                        .arg(setup.NetIOFont.weight() * 8)
                        .arg((setup.NetIOFont.italic() ? "italic" : "normal"));

                disp = head + disp + htmlTAIL;
                QTextDocument doc;
                doc.setPageSize(QSize(rc.width(),rc.height()));
                doc.setHtml(disp);
                doc.drawContents(p,rc);
            }
            else
            {
                disp = disp.replace("$CRLF","\n");

                p->setFont(setup.NetIOFont);
                p->setPen(setup.clShadowColor);
                rc.adjust(5,2,-5,-2);

                int F = Qt::AlignLeft | Qt::AlignVCenter;
                rc.adjust(1,1,1,1);
                p->drawText(rc,F,disp);
                rc.adjust(-2,-2,-2,-2);
                p->drawText(rc,F,disp);
                p->setPen(setup.NetIOTextColor);
                rc.adjust(1,1,1,1);
                p->drawText(rc,F,disp);
            }

            top = top + setup.NetIOHeight + 1;
        }
    }
    return top;
}

int MainWindow::dispGPUTemp(QPainter *p,int top)
{
    QString disp;
    QRect rc = QRect(0,top,this->geometry().width()-1,setup.GPUHeight);

    if(setup.bGPUDrawBack)   {p->setBrush(QBrush(setup.GPUBackColor,Qt::SolidPattern));}
    else                     {p->setBrush(Qt::NoBrush);}
    if(setup.bGPUDrawBorder) {p->setPen(setup.GPUBorderColor);}
    else                     {p->setPen(Qt::NoPen);}
    if(setup.bGPUDrawBack || setup.bGPUDrawBorder)
        p->drawRoundedRect(rc,3,3);

    disp = setup.GPUFormat;
    disp = disp.replace("$ver",gpuTemp.ver);
    disp = disp.replace("$free",gpuTemp.fmem);
    disp = disp.replace("$total",gpuTemp.tmem);
    disp = disp.replace("$temp",gpuTemp.temp);

    if(setup.bGPUHTML)
    {
        disp = disp.replace("$CRLF","<br>");
        QString head = QString(htmlHEAD).arg(setup.GPUFont.family())
                .arg(ColorToString(setup.GPUTextColor,false))
                .arg(setup.GPUFont.pointSizeF())
                .arg(setup.GPUFont.weight() * 8)
                .arg((setup.GPUFont.italic() ? "italic" : "normal"));

        disp = head + disp + htmlTAIL;
        QTextDocument doc;
        doc.setPageSize(QSize(rc.width(),rc.height()));
        doc.setHtml(disp);
        doc.drawContents(p,rc);
    }
    else
    {
        disp = disp.replace("$CRLF","\n");

        p->setFont(setup.GPUFont);
        p->setPen(setup.clShadowColor);
        rc.adjust(5,2,-5,-2);

        int F = Qt::AlignVCenter | Qt::AlignCenter;
        rc.adjust(1,1,1,1);
        p->drawText(rc,F,disp);
        rc.adjust(-2,-2,-2,-2);
        p->drawText(rc,F,disp);
        p->setPen(setup.GPUTextColor);
        rc.adjust(1,1,1,1);
        p->drawText(rc,F,disp);
    }
    top = top + setup.GPUHeight + 1;

    return top;
}

int MainWindow::dispCaption(QPainter *p,int top)
{
    QRect rc = QRect(0,top,this->geometry().width()-1,setup.GPUHeight);

    if(setup.bGPUDrawBack)   p->setBrush(QBrush(setup.GPUBackColor));
    else                        p->setBrush(Qt::NoBrush);
    if(setup.bGPUDrawBorder) p->setPen(setup.GPUBorderColor);
    else                        p->setPen(Qt::NoPen);
    if(setup.bGPUDrawBack || setup.bGPUDrawBorder)
        p->drawRoundedRect(rc,3,3);

    QString disp = tr("My Monitor");

    p->setFont(setup.GPUFont);
    p->setPen(setup.clShadowColor);
    rc.adjust(5,2,-5,-2);

    int F = Qt::AlignVCenter | Qt::AlignCenter;
    rc.adjust(1,1,1,1);
    p->drawText(rc,F,disp);
    rc.adjust(-2,-2,-2,-2);
    p->drawText(rc,F,disp);
    p->setPen(setup.ProcListTextColor);
    rc.adjust(1,1,1,1);
    p->drawText(rc,F,disp);

    top = top + setup.GPUHeight + 1;

    return top;
}

int MainWindow::dispProcessList(QPainter *p,int top)
{

    QRect rc = QRect(0,top,this->geometry().width()-1,setup.ProcListHeight);

    if(setup.bProcListDrawBack)   p->setBrush(QBrush(setup.ProcListBackColor));
    else                        p->setBrush(Qt::NoBrush);
    if(setup.bProcListDrawBorder) p->setPen(setup.ProcListBorderColor);
    else                        p->setPen(Qt::NoPen);
    if(setup.bProcListDrawBack || setup.bProcListDrawBorder)
        p->drawRoundedRect(rc,3,3);

    int cnt = 0,i = 0;

    QString disp = tr("Tasks : ")+QString::number(ProcessCount) + tr("\n") +
            tr(" CPU%    PID CMD");

    ProcList.sort();

    for(i = ProcList.count() - 1; i >= 0 ; i--)
    {
        disp = disp + "\n" + ProcList.at(i);
        if(++cnt > setup.ProcListCount) break;
    }

    if(setup.bProcListHTML)
    {
        QString head = QString(htmlHEAD).arg(setup.ProcListFont.family())
                .arg(ColorToString(setup.ProcListTextColor,false))
                .arg(setup.ProcListFont.pointSizeF())
                .arg(setup.ProcListFont.weight() * 8)
                .arg((setup.ProcListFont.italic() ? "italic" : "normal"));

        disp = head + disp + htmlTAIL;
        QTextDocument doc;
        doc.setPageSize(QSize(rc.width(),rc.height()));
        doc.setHtml(disp);
        doc.drawContents(p,rc);
    }
    else
    {
        p->setFont(setup.ProcListFont);
        p->setPen(setup.clShadowColor);
        rc.adjust(5,2,-5,-2);

        int F = Qt::AlignLeft | Qt::AlignTop;
        rc.adjust(1,1,1,1);
        p->drawText(rc,F,disp);
        rc.adjust(-2,-2,-2,-2);
        p->drawText(rc,F,disp);
        p->setPen(setup.ProcListTextColor);
        rc.adjust(1,1,1,1);
        p->drawText(rc,F,disp);
    }
    top = top + setup.ProcListHeight + 1;

    return top;
}
